package com.et.launch.push

import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.text.TextUtils
import android.util.Base64
import android.util.Log
import com.et.launch.BuildConfig
import com.et.launch.IPushAidl
import com.et.launch.IPushCallbackAidl
import com.et.launch.globalUuid
import com.et.launch.push.Tool.macSignature
import com.et.logger.ELog
import com.et.logger.TAGS
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.*
import java.nio.charset.Charset
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

class PushService : Service() {

    private var pushBinder: PushBinder? = null

    override fun onCreate() {
        super.onCreate()
        ELog.d(TAGS.launch, "launch:push-service onCreate")
    }

    override fun onBind(intent: Intent): IBinder {
        ELog.d(TAGS.launch, "launch:push-service onBind")
        return PushBinder(applicationContext).also {
            pushBinder?.exit()
            pushBinder = it
        }
    }

    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
        ELog.d(TAGS.launch, "launch:push-service onReBind")
    }

    override fun onDestroy() {
        super.onDestroy()
        ELog.d(TAGS.launch, "Push Service onDestroy")
    }

    class PushBinder(private val context: Context) : IPushAidl.Stub() {

        private var clientMqtt: MqttAndroidClient? = null

        override fun subscribe(callback: IPushCallbackAidl?, client: String?, uuid: String?) {
            uuid?.let {
                globalUuid = uuid // global uuid set value
                ELog.d(TAGS.launch, "launch subscribe")
                if (TextUtils.equals(connectedUuid, uuid)) return
                connectedUuid = uuid
                MqttAndroidClient(
                    context,
                    Config.serverUri,
                    "${Config.clientIdHead}$uuid-01"
                ).apply {
                    clientMqtt = this
                    MqttSimple.getInstance().clientId = "${Config.clientIdHead}$uuid-01"
                    MqttSimple.getInstance().connect(this, uuid)
                }
            }
        }

        fun exit() {
            clientMqtt?.unregisterResources()
        }
    }

    companion object {
        var connectedUuid = ""
    }
}

object Config {
    const val serverUri = BuildConfig.serverUri
    const val clientIdHead = BuildConfig.clientId
    const val instanceId = BuildConfig.instanceId
    const val accessKey = BuildConfig.accessKey
    const val secretKey = BuildConfig.secretKey
    const val topicParent = BuildConfig.topic
}

object Tool {
    @Throws(
        InvalidKeyException::class,
        NoSuchAlgorithmException::class
    )
    fun macSignature(text: String, secretKey: String): String {
        val charset = Charset.forName("UTF-8")
        val algorithm = "HmacSHA1"
        val mac = Mac.getInstance(algorithm)
        mac.init(SecretKeySpec(secretKey.toByteArray(charset), algorithm))
        val bytes = mac.doFinal(text.toByteArray(charset))
        return String(Base64.encode(bytes, Base64.NO_WRAP), charset)
    }
}

class MqttSimple {

    private var mqttAndroidClient: MqttAndroidClient? = null
    var clientId: String = Config.clientIdHead

    fun connect(client: MqttAndroidClient, uuid: String) {
        unRegister()
        mqttAndroidClient = client
        client.let { mqttAndroidClient ->
            mqttAndroidClient.setCallback(object : MqttCallbackExtended {
                override fun connectComplete(reconnect: Boolean, serverURI: String) {
                    ELog.d(TAGS.machine, "launch:connect complete")
                    subscribeToTopic(uuid) // re sub when connect success
                }

                override fun connectionLost(cause: Throwable) {
                    ELog.d(TAGS.machine, "connectionLost")
                    if (BuildConfig.DEBUG) Log.e("tag_mqtt", "Loss Mqtt Connect:", cause)
                }

                override fun messageArrived(topic: String, message: MqttMessage) {
                    val body = String(message.payload)
                    ELog.d(TAGS.machine, "launch:message arrived:$body")
                    MessageCenter.distribute(body)
                }

                override fun deliveryComplete(token: IMqttDeliveryToken) {}
            })

            val mqttConnectOptions = MqttConnectOptions()
            mqttConnectOptions.connectionTimeout = 1200
            mqttConnectOptions.keepAliveInterval = 60
            mqttConnectOptions.isAutomaticReconnect = true
            mqttConnectOptions.isCleanSession = true
            mqttConnectOptions.mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1
            try {
                mqttConnectOptions.userName =
                    "Signature|" + Config.accessKey + "|" + Config.instanceId
                mqttConnectOptions.password =
                    macSignature(clientId, Config.secretKey).toCharArray()
            } catch (e: Exception) {
                Log.e("exception", "setPassword", e)
            }
            try {
                mqttAndroidClient.connect(mqttConnectOptions, null, object : IMqttActionListener {
                    override fun onSuccess(asyncActionToken: IMqttToken) {
                        Log.w("connect", "launch:mqtt connect success")
                        subscribeToTopic(uuid)
                    }

                    override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                        Log.e("connect", "launch:mqtt connect onFailure", exception)
                    }
                })
            } catch (e: MqttException) {
                Log.e("connect", "exception", e)
            }
        }
    }

    fun subscribeToTopic(uuid: String) {
        try {
            val topicFilter = arrayOf("${Config.topicParent}/$uuid-01")
            val qos = intArrayOf(1)
            mqttAndroidClient?.subscribe(topicFilter, qos, null, object : IMqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    Log.w("subscribe", "launch:subscribe success>>${topicFilter[0]}")
//                    publishMessage()
                }

                override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                    Log.e("subscribe", "failed", exception)
                }
            })
        } catch (ex: MqttException) {
            Log.e("subscribe", "exception", ex)
        }
    }

    fun publishMessage() {
        try {
            val message = MqttMessage()
            val msg = "msg_test"
            message.payload = msg.toByteArray()
            mqttAndroidClient?.publish(
                "${Config.topicParent}_export",
                message,
                null,
                object : IMqttActionListener {
                    override fun onSuccess(asyncActionToken: IMqttToken) {
                        Log.w("publish", "success:$msg")
                    }

                    override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                        Log.w("publish", "failed:$msg")
                    }
                })
        } catch (e: MqttException) {
            Log.e("publish", "exception", e)
        }
    }

    private fun unRegister() {
        mqttAndroidClient?.apply {
            ELog.d(TAGS.launch, "Push Service unRegister")
            unregisterResources()
            mqttAndroidClient = null
        }
    }

    companion object {
        @JvmStatic
        fun getInstance() = Holder.holder
    }

    object Holder {
        val holder = MqttSimple()
    }
}

